using System;
using CodePlex.TfsLibrary.ObjectModel;
using CodePlex.TfsLibrary.RepositoryWebSvc;
using Xunit;

namespace CodePlex.TfsLibrary.ClientEngine
{
    public class TfsEngineRevertTest
    {
        public class Revert
        {
            [Fact]
            public void ItemNotTrackedAndNotPresent()
            {
                SourceItem callbackItem = null;
                SourceItemResult callbackResult = SourceItemResult.S_Ok;
                TestableTfsEngine engine = TestableTfsEngine.Create();

                engine.Revert(@"C:\Biff.txt", false, delegate(SourceItem item,
                                                              SourceItemResult result)
                                                     {
                                                         callbackItem = item;
                                                         callbackResult = result;
                                                     });

                Assert.Equal(@"C:\Biff.txt", callbackItem.LocalName);
                Assert.Equal(SourceItemResult.E_PathNotFound, callbackResult);
            }

            [Fact]
            public void NullLocalPath()
            {
                TestableTfsEngine engine = TestableTfsEngine.Create();

                Assert.Throws<ArgumentNullException>(delegate
                                                     {
                                                         engine.Revert(null, false, delegate {});
                                                     });
            }
        }

        public class Revert_File
        {
            [Fact]
            public void AddedFile()
            {
                SourceItem callbackItem = null;
                TestableTfsEngine engine = TestableTfsEngine.Create();
                engine.FileSystem.EnsurePath(@"C:\Foo");
                engine.FileSystem.WriteAllText(@"C:\Foo\bar.txt", "Text");
                engine.TfsState.TrackFolder("http://tfsUrl", "$/", @"C:\Foo", 0, 0, SourceItemStatus.Unmodified);
                engine.TfsState.TrackFile(@"C:\Foo\bar.txt", 1, 2, SourceItemStatus.Add);

                engine.Revert(@"C:\Foo\bar.txt", false, delegate(SourceItem item,
                                                                 SourceItemResult result)
                                                        {
                                                            callbackItem = item;
                                                        });

                Assert.Equal(SourceItemStatus.Add, callbackItem.LocalItemStatus);
                Assert.Equal(SourceItemStatus.Unversioned, engine.TfsState.GetSourceItem(@"C:\Foo\bar.txt").LocalItemStatus);
            }

            [Fact]
            public void ConflictedFile()
            {
                SourceItem callbackItem = null;
                TestableTfsEngine engine = TestableTfsEngine.Create();
                engine.FileSystem.EnsurePath(@"C:\Foo");
                engine.FileSystem.WriteAllText(@"C:\Foo\bar.txt", "Text from 3");
                engine.FileSystem.WriteAllText(@"C:\Foo\bar.txt.r3", "Text from 3");
                engine.FileSystem.WriteAllText(@"C:\Foo\bar.txt.r10", "Text from 10");
                engine.FileSystem.WriteAllText(@"C:\Foo\bar.txt.mine", "Text from mine");
                engine.TfsState.TrackFolder("http://tfsUrl", "$/", @"C:\Foo", 0, 0, SourceItemStatus.Unmodified);
                engine.TfsState.TrackFile(@"C:\Foo\bar.txt", 1, 3, SourceItemStatus.Unmodified);
                engine.TfsState.MarkFileAsConflicted(@"C:\Foo\bar.txt", @"C:\Foo\bar.txt.r10", 10);
                engine.FileSystem.DeleteFile(@"C:\Foo\bar.txt");

                engine.Revert(@"C:\Foo\bar.txt", false, delegate(SourceItem item,
                                                                 SourceItemResult result)
                                                        {
                                                            callbackItem = item;
                                                        });

                Assert.Equal(SourceItemStatus.Conflict, callbackItem.LocalItemStatus);

                SourceItem revertedItem = engine.TfsState.GetSourceItem(@"C:\Foo\bar.txt");
                Assert.Equal(SourceItemStatus.Unmodified, revertedItem.LocalItemStatus);
                Assert.Equal(10, revertedItem.LocalChangesetId);
                Assert.Equal(Constants.NullChangesetId, revertedItem.LocalConflictChangesetId);
                Assert.Equal("Text from 10", engine.FileSystem.ReadAllText(@"C:\Foo\bar.txt"));
                Assert.False(engine.FileSystem.FileExists(@"C:\Foo\bar.txt.r3"));
                Assert.False(engine.FileSystem.FileExists(@"C:\Foo\bar.txt.r10"));
                Assert.False(engine.FileSystem.FileExists(@"C:\Foo\bar.txt.mine"));
            }

            [Fact]
            public void DeletedFile()
            {
                SourceItem callbackItem = null;
                TestableTfsEngine engine = TestableTfsEngine.Create();
                engine.FileSystem.EnsurePath(@"C:\Foo");
                engine.FileSystem.WriteAllText(@"C:\Foo\bar.txt", "Text");
                engine.TfsState.TrackFolder("http://tfsUrl", "$/", @"C:\Foo", 0, 0, SourceItemStatus.Unmodified);
                engine.TfsState.TrackFile(@"C:\Foo\bar.txt", 1, 2, SourceItemStatus.Unmodified);
                engine.TfsState.TrackFile(@"C:\Foo\bar.txt", 1, 2, SourceItemStatus.Delete);
                engine.FileSystem.DeleteFile(@"C:\Foo\bar.txt");

                engine.Revert(@"C:\Foo\bar.txt", false, delegate(SourceItem item,
                                                                 SourceItemResult result)
                                                        {
                                                            callbackItem = item;
                                                        });

                Assert.Equal(SourceItemStatus.Delete, callbackItem.LocalItemStatus);
                Assert.Equal(SourceItemStatus.Unmodified, engine.TfsState.GetSourceItem(@"C:\Foo\bar.txt").LocalItemStatus);
            }

            [Fact]
            public void FileNotUnderSourceControl()
            {
                SourceItem callbackItem = null;
                SourceItemResult callbackResult = SourceItemResult.S_Ok;
                TestableTfsEngine engine = TestableTfsEngine.Create();
                engine.FileSystem.EnsurePath(@"C:\Foo");
                engine.FileSystem.WriteAllText(@"C:\Foo\bar.txt", "Text");
                engine.TfsState.TrackFolder("http://tfsUrl", "$/", @"C:\Foo", 0, 0, SourceItemStatus.Unmodified);

                engine.Revert(@"C:\Foo\bar.txt", false, delegate(SourceItem item,
                                                                 SourceItemResult result)
                                                        {
                                                            callbackItem = item;
                                                            callbackResult = result;
                                                        });

                Assert.Equal(@"C:\Foo\bar.txt", callbackItem.LocalName);
                Assert.Equal(SourceItemStatus.Unversioned, callbackItem.LocalItemStatus);
                Assert.Equal(SourceItemResult.E_NotUnderSourceControl, callbackResult);
            }

            [Fact]
            public void MissingFile()
            {
                SourceItem callbackItem = null;
                TestableTfsEngine engine = TestableTfsEngine.Create();
                engine.FileSystem.EnsurePath(@"C:\Foo");
                engine.FileSystem.WriteAllText(@"C:\Foo\bar.txt", "Text");
                engine.TfsState.TrackFolder("http://tfsUrl", "$/", @"C:\Foo", 0, 0, SourceItemStatus.Unmodified);
                engine.TfsState.TrackFile(@"C:\Foo\bar.txt", 1, 2, SourceItemStatus.Unmodified);
                engine.FileSystem.DeleteFile(@"C:\Foo\bar.txt");

                engine.Revert(@"C:\Foo\bar.txt", false, delegate(SourceItem item,
                                                                 SourceItemResult result)
                                                        {
                                                            callbackItem = item;
                                                        });

                Assert.Equal(SourceItemStatus.Missing, callbackItem.LocalItemStatus);
                Assert.Equal(SourceItemStatus.Unmodified, engine.TfsState.GetSourceItem(@"C:\Foo\bar.txt").LocalItemStatus);
            }

            [Fact]
            public void ModifiedFile()
            {
                SourceItem callbackItem = null;
                SourceItemResult callbackResult = SourceItemResult.S_Ok;
                TestableTfsEngine engine = TestableTfsEngine.Create();
                engine.FileSystem.EnsurePath(@"C:\Foo");
                engine.FileSystem.WriteAllText(@"C:\Foo\bar.txt", "Text");
                engine.TfsState.TrackFolder("http://tfsUrl", "$/", @"C:\Foo", 0, 0, SourceItemStatus.Unmodified);
                engine.TfsState.TrackFile(@"C:\Foo\bar.txt", 1, 2, SourceItemStatus.Unmodified);
                engine.FileSystem.WriteAllText(@"C:\Foo\bar.txt", "Biff!");

                engine.Revert(@"C:\Foo\bar.txt", false, delegate(SourceItem item,
                                                                 SourceItemResult result)
                                                        {
                                                            callbackItem = item;
                                                            callbackResult = result;
                                                        });

                Assert.Equal(@"C:\Foo\bar.txt", callbackItem.LocalName);
                Assert.Equal(1, callbackItem.ItemId);
                Assert.Equal(2, callbackItem.LocalChangesetId);
                Assert.Equal(SourceItemStatus.Modified, callbackItem.LocalItemStatus);
                Assert.Equal("Text", engine.FileSystem.ReadAllText(@"C:\Foo\bar.txt"));
                Assert.Equal(SourceItemStatus.Unmodified, engine.TfsState.GetSourceItem(@"C:\Foo\bar.txt").LocalItemStatus);
                Assert.Equal(SourceItemResult.S_Ok, callbackResult);
            }

            [Fact]
            public void ParentFolderNotTracked()
            {
                SourceItem callbackItem = null;
                SourceItemResult callbackResult = SourceItemResult.S_Ok;
                TestableTfsEngine engine = TestableTfsEngine.Create();
                engine.FileSystem.EnsurePath(@"C:\Foo");
                engine.FileSystem.WriteAllText(@"C:\Foo\bar.txt", "Text");

                engine.Revert(@"C:\Foo\bar.txt", false, delegate(SourceItem item,
                                                                 SourceItemResult result)
                                                        {
                                                            callbackItem = item;
                                                            callbackResult = result;
                                                        });

                Assert.Equal(@"C:\Foo\bar.txt", callbackItem.LocalName);
                Assert.Equal(SourceItemResult.E_NotInAWorkingFolder, callbackResult);
            }

            [Fact]
            public void UnmodifiedFile()
            {
                TestableTfsEngine engine = TestableTfsEngine.Create();
                engine.FileSystem.EnsurePath(@"C:\Foo");
                engine.FileSystem.WriteAllText(@"C:\Foo\bar.txt", "Text");
                engine.TfsState.TrackFolder("http://tfsUrl", "$/", @"C:\Foo", 0, 0, SourceItemStatus.Unmodified);
                engine.TfsState.TrackFile(@"C:\Foo\bar.txt", 1, 2, SourceItemStatus.Unmodified);

                engine.Revert(@"C:\Foo\bar.txt", false, delegate
                                                        {
                                                            throw new InvalidOperationException("Should not be called back");
                                                        });

                Assert.Equal(SourceItemStatus.Unmodified, engine.TfsState.GetSourceItem(@"C:\Foo\bar.txt").LocalItemStatus);
            }

            [Fact]
            public void VerifiesDirectory()
            {
                TestableTfsEngine engine = TestableTfsEngine.Create();
                engine.FileSystem.EnsurePath(@"C:\Foo");
                engine.FileSystem.WriteAllText(@"C:\Foo\bar.txt", "Text");
                engine.TfsState.TrackFolder("http://tfsUrl", "$/", @"C:\Foo", 0, 0, SourceItemStatus.Unmodified);
                engine.TfsState.TrackFile(@"C:\Foo\bar.txt", 1, 2, SourceItemStatus.Unmodified);

                engine.Revert(@"C:\Foo\bar.txt", false, null);

                Assert.Equal(@"C:\Foo", engine.ValidateDirectoryStructure_Directory);
            }
        }

        public class Revert_Folder
        {
            [Fact]
            public void FolderNotUnderSourceControl()
            {
                SourceItem callbackItem = null;
                SourceItemResult callbackResult = SourceItemResult.S_Ok;
                TestableTfsEngine engine = TestableTfsEngine.Create();
                engine.FileSystem.EnsurePath(@"C:\Foo");
                engine.FileSystem.EnsurePath(@"C:\Foo\Bar");
                engine.TfsState.TrackFolder("http://tfsUrl", "$/", @"C:\Foo", 0, 0, SourceItemStatus.Unmodified);

                engine.Revert(@"C:\Foo\Bar", false, delegate(SourceItem item,
                                                             SourceItemResult result)
                                                    {
                                                        callbackItem = item;
                                                        callbackResult = result;
                                                    });

                Assert.Equal(@"C:\Foo\Bar", callbackItem.LocalName);
                Assert.Equal(ItemType.Folder, callbackItem.ItemType);
                Assert.Equal(SourceItemResult.E_NotUnderSourceControl, callbackResult);
            }

            [Fact]
            public void RevertAddedFolder()
            {
                SourceItem callbackItem = null;
                TestableTfsEngine engine = TestableTfsEngine.Create();
                engine.FileSystem.EnsurePath(@"C:\Foo");
                engine.FileSystem.EnsurePath(@"C:\Foo\Bar");
                engine.TfsState.TrackFolder("http://tfsUrl", "$/", @"C:\Foo", 0, 0, SourceItemStatus.Unmodified);
                engine.TfsState.TrackFolder("http://tfsUrl", "$/Bar", @"C:\Foo\Bar", 0, 0, SourceItemStatus.Add);

                engine.Revert(@"C:\Foo", true, delegate(SourceItem item,
                                                        SourceItemResult result)
                                               {
                                                   callbackItem = item;
                                               });

                Assert.Equal(SourceItemStatus.Add, callbackItem.LocalItemStatus);
                Assert.Equal(SourceItemStatus.Unversioned, engine.TfsState.GetSourceItem(@"C:\Foo\Bar").LocalItemStatus);
            }

            [Fact]
            public void RevertDeletedFile()
            {
                TestableTfsEngine engine = TestableTfsEngine.Create();
                engine.FileSystem.EnsurePath(@"C:\Foo");
                engine.FileSystem.WriteAllText(@"C:\Foo\baz.txt", "To be deleted");
                engine.TfsState.TrackFolder("http://tfsUrl", "$/", @"C:\Foo", 0, 0, SourceItemStatus.Unmodified);
                engine.TfsState.TrackFile(@"C:\Foo\baz.txt", 1, 2, SourceItemStatus.Unmodified);
                engine.TfsState.TrackFile(@"C:\Foo\baz.txt", 1, 2, SourceItemStatus.Delete);
                engine.FileSystem.DeleteFile(@"C:\Foo\baz.txt");

                engine.Revert(@"C:\Foo", false, null);

                Assert.Equal("To be deleted", engine.FileSystem.ReadAllText(@"C:\Foo\baz.txt"));
            }

            [Fact]
            public void RevertDeletedFolder()
            {
                SourceItem callbackItem = null;
                TestableTfsEngine engine = TestableTfsEngine.Create();
                engine.FileSystem.EnsurePath(@"C:\Foo");
                engine.FileSystem.EnsurePath(@"C:\Foo\Bar");
                engine.TfsState.TrackFolder("http://tfsUrl", "$/", @"C:\Foo", 0, 0, SourceItemStatus.Unmodified);
                engine.TfsState.TrackFolder("http://tfsUrl", "$/Bar", @"C:\Foo\Bar", 0, 0, SourceItemStatus.Delete);

                engine.Revert(@"C:\Foo", true, delegate(SourceItem item,
                                                        SourceItemResult result)
                                               {
                                                   callbackItem = item;
                                               });

                Assert.Equal(SourceItemStatus.Delete, callbackItem.LocalItemStatus);
                Assert.Equal(SourceItemStatus.Unmodified, engine.TfsState.GetSourceItem(@"C:\Foo\Bar").LocalItemStatus);
            }

            [Fact]
            public void RevertMissingFolder()
            {
                SourceItem callbackItem = null;
                TestableTfsEngine engine = TestableTfsEngine.Create();
                engine.FileSystem.EnsurePath(@"C:\Foo");
                engine.FileSystem.EnsurePath(@"C:\Foo\Bar");
                engine.TfsState.TrackFolder("http://tfsUrl", "$/", @"C:\Foo", 0, 0, SourceItemStatus.Unmodified);
                engine.TfsState.TrackFolder("http://tfsUrl", "$/", @"C:\Foo\Bar", 0, 0, SourceItemStatus.Unmodified);
                engine.FileSystem.DeleteDirectory(@"C:\Foo\Bar", true);

                engine.Revert(@"C:\Foo\Bar", false, delegate(SourceItem item,
                                                             SourceItemResult result)
                                                    {
                                                        callbackItem = item;
                                                    });

                Assert.Equal(SourceItemStatus.Missing, callbackItem.LocalItemStatus);
            }

            [Fact]
            public void RevertModified()
            {
                int revertCount = 0;
                TestableTfsEngine engine = TestableTfsEngine.Create();
                engine.FileSystem.EnsurePath(@"C:\Foo");
                engine.FileSystem.EnsurePath(@"C:\Foo\Bar");
                engine.FileSystem.WriteAllText(@"C:\Foo\baz.txt", "To be modified");
                engine.FileSystem.WriteAllText(@"C:\Foo\Bar\biff.txt", "Also to be modified");
                engine.TfsState.TrackFolder("http://tfsUrl", "$/", @"C:\Foo", 0, 0, SourceItemStatus.Unmodified);
                engine.TfsState.TrackFolder("http://tfsUrl", "$/Bar", @"C:\Foo\Bar", 0, 0, SourceItemStatus.Unmodified);
                engine.TfsState.TrackFile(@"C:\Foo\baz.txt", 1, 2, SourceItemStatus.Unmodified);
                engine.TfsState.TrackFile(@"C:\Foo\Bar\biff.txt", 3, 4, SourceItemStatus.Unmodified);
                engine.FileSystem.WriteAllText(@"C:\Foo\baz.txt", "Modified");
                engine.FileSystem.WriteAllText(@"C:\Foo\Bar\biff.txt", "Modified");

                engine.Revert(@"C:\Foo", false, delegate
                                                {
                                                    ++revertCount;
                                                });

                Assert.Equal(1, revertCount);
                Assert.Equal("To be modified", engine.FileSystem.ReadAllText(@"C:\Foo\baz.txt"));
                Assert.Equal("Modified", engine.FileSystem.ReadAllText(@"C:\Foo\Bar\biff.txt"));
            }

            [Fact]
            public void RevertModifiedRecursive()
            {
                int revertCount = 0;
                TestableTfsEngine engine = TestableTfsEngine.Create();
                engine.FileSystem.EnsurePath(@"C:\Foo");
                engine.FileSystem.EnsurePath(@"C:\Foo\Bar");
                engine.FileSystem.WriteAllText(@"C:\Foo\baz.txt", "To be modified");
                engine.FileSystem.WriteAllText(@"C:\Foo\Bar\biff.txt", "Also to be modified");
                engine.TfsState.TrackFolder("http://tfsUrl", "$/", @"C:\Foo", 0, 0, SourceItemStatus.Unmodified);
                engine.TfsState.TrackFolder("http://tfsUrl", "$/Bar", @"C:\Foo\Bar", 0, 0, SourceItemStatus.Unmodified);
                engine.TfsState.TrackFile(@"C:\Foo\baz.txt", 1, 2, SourceItemStatus.Unmodified);
                engine.TfsState.TrackFile(@"C:\Foo\Bar\biff.txt", 3, 4, SourceItemStatus.Unmodified);
                engine.FileSystem.WriteAllText(@"C:\Foo\baz.txt", "Modified");
                engine.FileSystem.WriteAllText(@"C:\Foo\Bar\biff.txt", "Modified");

                engine.Revert(@"C:\Foo", true, delegate
                                               {
                                                   ++revertCount;
                                               });

                Assert.Equal(2, revertCount);
                Assert.Equal("To be modified", engine.FileSystem.ReadAllText(@"C:\Foo\baz.txt"));
                Assert.Equal("Also to be modified", engine.FileSystem.ReadAllText(@"C:\Foo\Bar\biff.txt"));
            }

            [Fact]
            public void VerifiesDirectory()
            {
                TestableTfsEngine engine = TestableTfsEngine.Create();
                engine.FileSystem.EnsurePath(@"C:\Foo");
                engine.TfsState.TrackFolder("http://tfsUrl", "$/", @"C:\Foo", 0, 0, SourceItemStatus.Unmodified);

                engine.Revert(@"C:\Foo", false, null);

                Assert.Equal(@"C:\Foo", engine.ValidateDirectoryStructure_Directory);
            }
        }
    }
}